//*****************************************************************************
//
//! \file socket.c
//! \brief SOCKET APIs Implements file.
//! \details SOCKET APIs like as Berkeley Socket APIs. 
//! \version 1.0.3
//! \date 2013/10/21
//! \par  Revision history
//!       <2015/02/05> Notice
//!        The version history is not updated after this point.
//!        Download the latest version directly from GitHub. Please visit the our GitHub repository for ioLibrary.
//!        >> https://github.com/Wiznet/ioLibrary_Driver
//!       <2014/05/01> V1.0.3. Refer to M20140501
//!         1. Implicit type casting -> Explicit type casting.
//!         2. replace 0x01 with PACK_REMAINED in recvfrom()
//!         3. Validation a destination ip in connect() & sendto(): 
//!            It occurs a fatal error on converting unint32 address if uint8* addr parameter is not aligned by 4byte address.
//!            Copy 4 byte addr value into temporary uint32 variable and then compares it.
//!       <2013/12/20> V1.0.2 Refer to M20131220
//!                    Remove Warning.
//!       <2013/11/04> V1.0.1 2nd Release. Refer to "20131104".
//!                    In sendto(), Add to clear timeout interrupt status (Sn_IR_TIMEOUT)
//!       <2013/10/21> 1st Release
//! \author MidnightCow
//! \copyright
//!
//! Copyright (c)  2013, WIZnet Co., LTD.
//! All rights reserved.
//! 
//! Redistribution and use in source and binary forms, with or without 
//! modification, are permitted provided that the following conditions 
//! are met: 
//! 
//!     * Redistributions of source code must retain the above copyright 
//! notice, this list of conditions and the following disclaimer. 
//!     * Redistributions in binary form must reproduce the above copyright
//! notice, this list of conditions and the following disclaimer in the
//! documentation and/or other materials provided with the distribution. 
//!     * Neither the name of the <ORGANIZATION> nor the names of its 
//! contributors may be used to endorse or promote products derived 
//! from this software without specific prior written permission. 
//! 
//! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
//! AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
//! IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
//! ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
//! LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
//! CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
//! SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
//! INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
//! CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
//! ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
//! THE POSSIBILITY OF SUCH DAMAGE.
//
//*****************************************************************************
#include "socket.h"
//M20150401 : Typing Error
//#define SOCK_ANY_PORT_NUM  0xC000;
#define SOCK_ANY_PORT_NUM  0xC000

static uint16_t sock_any_port = SOCK_ANY_PORT_NUM;
static uint16_t sock_io_mode = 0;
static uint16_t sock_is_sending = 0;

static uint16_t sock_remained_size[_WIZCHIP_SOCK_NUM_] = {0,0,};

//M20150601 : For extern decleation
//static uint8_t  sock_pack_info[_WIZCHIP_SOCK_NUM_] = {0,};
uint8_t  sock_pack_info[_WIZCHIP_SOCK_NUM_] = {0,};
//

#if _WIZCHIP_ == 5200
   static uint16_t sock_next_rd[_WIZCHIP_SOCK_NUM_] ={0,};
#endif

//A20150601 : For integrating with W5300
#if _WIZCHIP_ == 5300
   uint8_t sock_remained_byte[_WIZCHIP_SOCK_NUM_] = {0,}; // set by wiz_recv_data()
#endif


#define CHECK_SOCKNUM()   \
   do{                    \
      if(sn > _WIZCHIP_SOCK_NUM_) return SOCKERR_SOCKNUM;   \
   }while(0);             \

#define CHECK_SOCKMODE(mode)  \
   do{                     \
      if((getSn_MR(sn) & 0x0F) != mode) return SOCKERR_SOCKMODE;  \
   }while(0);              \

#define CHECK_TCPMODE()                                           \
   do{                                                            \
      if((getSn_MR(sn) & 0x03) != 0x01) return SOCKERR_SOCKMODE;  \
   }while(0);

#define CHECK_SOCKINIT()   \
   do{                     \
      if((getSn_SR(sn) != SOCK_INIT)) return SOCKERR_SOCKINIT; \
   }while(0);              \

#define CHECK_SOCKDATA()   \
   do{                     \
      if(len == 0) return SOCKERR_DATALEN;   \
   }while(0);              \
//teddy 240122
#if _WIZCHIP_ == W6100 || _WIZCHIP_ == W6300
#define CHECK_TCPMODE()                                           \
   do{                                                            \
      if((getSn_MR(sn) & 0x03) != 0x01) return SOCKERR_SOCKMODE;  \
   }while(0);

#define CHECK_UDPMODE()                                           \
   do{                                                            \
      if((getSn_MR(sn) & 0x03) != 0x02) return SOCKERR_SOCKMODE;  \
   }while(0);

#define CHECK_IPMODE()                                            \
   do{                                                            \
      if((getSn_MR(sn) & 0x07) != 0x03) return SOCKERR_SOCKMODE;  \
   }while(0);

#define CHECK_DGRAMMODE()                                         \
   do{                                                            \
      if(getSn_MR(sn) == Sn_MR_CLOSED) return SOCKERR_SOCKMODE;   \
      if((getSn_MR(sn) & 0x03) == 0x01) return SOCKERR_SOCKMODE;  \
   }while(0);

#define CHECK_IPZERO(addr, addrlen)                                  \
   do{                                                               \
      uint16_t ipzero= 0;                                            \
      for(uint8_t i=0; i<addrlen; i++)  ipzero += (uint16_t)addr[i]; \
      if (ipzero == 0) return SOCKERR_IPINVALID;                     \
   }while(0);


#endif


#if (_WIZCHIP_ == W5100 || _WIZCHIP_ == W5100S || _WIZCHIP_ == W5200 || _WIZCHIP_ == W5300 || _WIZCHIP_ == W5500)
#define IPV6_EN 0
#else 
#define IPV6_EN 1
#endif 

#if 1


#define Sn_MR_TCP4           (Sn_MR_TCP)   ///< Refer to @ref Sn_MR_TCP.
#define Sn_MR_UDP4           (Sn_MR_UDP)   ///< Refer to @ref Sn_MR_UDP
#define Sn_MR_IPRAW4         (Sn_MR_IPRAW)   ///< Refer to @ref Sn_MR_IPRAW.   
#define Sn_MR_TCP6           (0x09)
#define Sn_MR_UDP6           (0x0A) //0x1010
#define Sn_MR_IPRAW6         (0x0B) //0x1011
#define Sn_MR_TCPD           (0x0D)
#define Sn_MR_UDPD           (0x0E)



#endif 


#if 0 // By lihan  
static uint8_t addrlenTEST = -1 ; 

void setAddrlen_W6x00( uint8_t num){
   addrlenTEST = num;  
}

uint8_t  checkAddrlen_W6x00( ){
   //if (addrlenTEST < 0 ) 
   if ((addrlenTEST != 4)  && (addrlenTEST !=16 )) 
      perror("Error: addrlen is not initialized"); 
   else
      printf( "addrlenTEST %d \r\n" ,addrlenTEST ) ; 
   return addrlenTEST;
}

inline void inline_setAddrlen_W6x00( uint8_t num){
#if (_WIZCHIP_ == 6100) || (_WIZCHIP_ == 6300)
   setAddrlen_W6x00(num);
#endif 
}

inline uint8_t inline_CheckAddrlen_W6x00( void ){
#if (_WIZCHIP_ == 6100) || (_WIZCHIP_ == 6300)
   return  checkAddrlen_W6x00();
#else 
   return 4;
#endif 
}
#endif




int8_t socket(uint8_t sn, uint8_t protocol, uint16_t port, uint8_t flag)
{ 

   uint8_t taddr[16];
   uint16_t local_port=0;
   CHECK_SOCKNUM(); 
   switch (protocol & 0x0F)
   {
#if IPV6_EN
      case Sn_MR_TCP4 :
         getSIPR(taddr);
         CHECK_IPZERO(taddr, 4);
         break;
      case Sn_MR_TCP6 :
         getLLAR(taddr);
         CHECK_IPZERO(taddr, 16);
         //getGUAR(taddr);
         //CHECK_IPZERO(taddr, 16);
         break;
      case Sn_MR_TCPD :  
         getSIPR(taddr);
         CHECK_IPZERO(taddr, 4);
         getLLAR(taddr);
         CHECK_IPZERO(taddr, 16);
         //getGUAR(taddr);
         //CHECK_IPZERO(taddr, 16);
         break;
#else  
         case Sn_MR_TCP :
         {
            uint8_t taddr;
            getSIPR((uint8_t*)&taddr);
            if(taddr == 0) return SOCKERR_SOCKINIT;
            break;
         }
#endif 
      case Sn_MR_UDP :
      case Sn_MR_UDP6 :
      case Sn_MR_UDPD :
      case Sn_MR_MACRAW :
      case Sn_MR_IPRAW4 :
      case Sn_MR_IPRAW6 :
         break; 
      default :
        return SOCKERR_SOCKMODE;
   } 

   if((flag & 0x04)) return SOCKERR_SOCKFLAG;
   if(flag != 0)
   {
      switch(protocol)
      {

#if IPV6_EN
         case Sn_MR_MACRAW:
            if((flag & (SF_DHA_MANUAL | SF_FORCE_ARP)) != 0)
            	return SOCKERR_SOCKFLAG;
            break;
         case Sn_MR_TCP4:
         case Sn_MR_TCP6:
         case Sn_MR_TCPD:     
            if((flag & (SF_MULTI_ENABLE | SF_UNI_BLOCK)) !=0)
            	return SOCKERR_SOCKFLAG;
            break;
         case Sn_MR_IPRAW4:
         case Sn_MR_IPRAW6:
            if(flag !=0)
            	return SOCKERR_SOCKFLAG;
            break;
#else 
   	   case Sn_MR_TCP:
   		  //M20150601 :  For SF_TCP_ALIGN & W5300
          #if _WIZCHIP_ == 5300
   		     if((flag & (SF_TCP_NODELAY|SF_IO_NONBLOCK|SF_TCP_ALIGN))==0) return SOCKERR_SOCKFLAG;
          #else
   		     if((flag & (SF_TCP_NODELAY|SF_IO_NONBLOCK))==0) return SOCKERR_SOCKFLAG;
          #endif

   	      break;
   	   case Sn_MR_UDP:
   	      if(flag & SF_IGMP_VER2)
   	      {
   	         if((flag & SF_MULTI_ENABLE)==0) return SOCKERR_SOCKFLAG;
   	      }
   	      #if _WIZCHIP_ == 5500
      	      if(flag & SF_UNI_BLOCK)
      	      {
      	         if((flag & SF_MULTI_ENABLE) == 0) return SOCKERR_SOCKFLAG;
      	      }
   	      #endif
         break;

#endif 

         default:
            break;
      }
   }
   close(sn);
   setSn_MR(sn,(protocol | (flag & 0xF0)));
#if IPV6_EN
   setSn_MR2(sn, flag & 0x03);  
#endif 
   if(!port)
   {
      port = sock_any_port++;
      if(sock_any_port == 0xFFF0) sock_any_port = SOCK_ANY_PORT_NUM;
   }
   setSn_PORTR(sn,port);
   setSn_CR(sn,Sn_CR_OPEN);

   while(getSn_CR(sn));

   sock_io_mode &= ~(1 <<sn);
   sock_io_mode |= ((flag & (SF_IO_NONBLOCK>>3)) << sn);
   sock_is_sending &= ~(1<<sn);
   sock_remained_size[sn] = 0;
   sock_pack_info[sn] = PACK_NONE;

   while(getSn_SR(sn) == SOCK_CLOSED) ;
//   printf("[%d]%d\r\n", sn, getSn_PORTR(sn));
   return sn;
}  


int8_t close(uint8_t sn)
{
   CHECK_SOCKNUM();
   setSn_CR(sn,Sn_CR_CLOSE);
   /* wait to process the command... */
   while( getSn_CR(sn) );
   /* clear all interrupt of SOCKETn. */
   setSn_IR(sn, 0xFF);  	
   //setSn_IRCLR(sn, 0xFF);  
   /* Release the sock_io_mode of SOCKETn. */
   sock_io_mode &= ~(1<<sn); 
   sock_remained_size[sn] = 0;
   sock_is_sending &= ~(1<<sn);
   sock_pack_info[sn] = PACK_NONE;
   while(getSn_SR(sn) != SOCK_CLOSED);
   return SOCK_OK;
}


int8_t listen(uint8_t sn)
{
   CHECK_SOCKNUM();
   CHECK_SOCKINIT();
   setSn_CR(sn,Sn_CR_LISTEN);
   while(getSn_CR(sn));
   while(getSn_SR(sn) != SOCK_LISTEN)
   {
      close(sn);
      return SOCKERR_SOCKCLOSED;
   }
   return SOCK_OK;
}
//int8_t connect (uint8_t sn, uint8_t * addr, uint16_t port )
int8_t connect_W5x00(uint8_t sn, uint8_t * addr, uint16_t port  ){
   printf(" W5x00 - connect - addrlen = %d \r\n" , 4 );
   // #if IPV6_En
   // #endif 
   return connect_IO_6(sn , addr , port, 4 );
}

int8_t connect_W6x00(uint8_t sn, uint8_t * addr, uint16_t port, uint8_t addrlen ){
   printf(" W6x00 - connect - addrlen = %d \r\n" , addrlen );
   // #if !(IPV6_En)
   // #endif 
   return connect_IO_6(sn , addr , port ,addrlen );
}

static int8_t connect_IO_6 (uint8_t sn, uint8_t * addr, uint16_t port, uint8_t addrlen )
{ 

   printf(" connect - addrlen = %d \r\n" , addrlen );

   CHECK_SOCKNUM();
   CHECK_TCPMODE(); // same macro " CHECK_SOCKMODE(Sn_MR_TCP);"
   CHECK_SOCKINIT();
  
#if IPV6_En
   CHECK_IPZERO(addr, addrlen);
#else
   uint32_t taddr;
   taddr = ((uint32_t)addr[0] & 0x000000FF);
   taddr = (taddr << 8) + ((uint32_t)addr[1] & 0x000000FF);
   taddr = (taddr << 8) + ((uint32_t)addr[2] & 0x000000FF);
   taddr = (taddr << 8) + ((uint32_t)addr[3] & 0x000000FF);
   if( taddr == 0xFFFFFFFF || taddr == 0) return SOCKERR_IPINVALID;
#endif 

   if(port == 0)
	   return SOCKERR_PORTZERO;

   setSn_DPORTR(sn, port);
  
   if (addrlen == 16)     // addrlen=16, Sn_MR_TCP6(1001), Sn_MR_TCPD(1101))
   {
#if (IPV6_EN) 
      if( getSn_MR(sn) & 0x08)     
      {
         setSn_DIP6R(sn,addr);
         setSn_CR(sn,Sn_CR_CONNECT6);
      }
      else 
#endif 
      return SOCKERR_SOCKMODE;
   } 
   else           // addrlen=4, Sn_MR_TCP4(0001), Sn_MR_TCPD(1101)
   {
      if(getSn_MR(sn) == Sn_MR_TCP6) return SOCKERR_SOCKMODE;
      setSn_DIPR(sn,addr);
      setSn_CR(sn,Sn_CR_CONNECT);
   }
   while(getSn_CR(sn));

   if(sock_io_mode & (1<<sn)) return SOCK_BUSY;

   while(getSn_SR(sn) != SOCK_ESTABLISHED)
   {
      if (getSn_IR(sn) & Sn_IR_TIMEOUT)
      {
         setSn_IR(sn, Sn_IR_TIMEOUT);
         return SOCKERR_TIMEOUT;
      }
      if (getSn_SR(sn) == SOCK_CLOSED)
      {
         return SOCKERR_SOCKCLOSED;
      }
   } 
   return SOCK_OK;
}

int8_t disconnect(uint8_t sn)
{
   CHECK_SOCKNUM();
   CHECK_TCPMODE();
   if(getSn_SR(sn) != SOCK_CLOSED)
   {
      setSn_CR(sn,Sn_CR_DISCON);
      /* wait to process the command... */
      while(getSn_CR(sn));
      if(sock_io_mode & (1<<sn)) return SOCK_BUSY;
      while(getSn_SR(sn) != SOCK_CLOSED)
      {
         if(getSn_IR(sn) & Sn_IR_TIMEOUT)
         {
            close(sn);
            return SOCKERR_TIMEOUT;
         }
      }
   }
   return SOCK_OK;
}


#if 1
int32_t send(uint8_t sn, uint8_t * buf, uint16_t len)
{
   uint8_t tmp=0;
   uint16_t freesize=0;
   /* 
    * The below codes can be omitted for optmization of speed
    */
   //CHECK_SOCKNUM();
   //CHECK_TCPMODE(Sn_MR_TCP4);
   /************/

   freesize = getSn_TxMAX(sn);
   if (len > freesize) len = freesize; // check size not to exceed MAX size.
   while(1)
   {
      freesize = (uint16_t)getSn_TX_FSR(sn);
      tmp = getSn_SR(sn);
      if ((tmp != SOCK_ESTABLISHED) && (tmp != SOCK_CLOSE_WAIT))
      {
         if(tmp == SOCK_CLOSED) close(sn);
         return SOCKERR_SOCKSTATUS;
      }
      if(len <= freesize) break;
      if( sock_io_mode & (1<<sn) ) return SOCK_BUSY;  
   }
   wiz_send_data(sn, buf, len);
   if(sock_is_sending & (1<<sn))
   {
      while ( !(getSn_IR(sn) & Sn_IR_SENDOK) )
      {    
         tmp = getSn_SR(sn);
         if ((tmp != SOCK_ESTABLISHED) && (tmp != SOCK_CLOSE_WAIT) )
         {
            if( (tmp == SOCK_CLOSED) || (getSn_IR(sn) & Sn_IR_TIMEOUT) ) close(sn);
            return SOCKERR_SOCKSTATUS;
         }
         if(sock_io_mode & (1<<sn)) return SOCK_BUSY;
      } 
      setSn_IR(sn, Sn_IR_SENDOK);
   }
   setSn_CR(sn,Sn_CR_SEND);
 
   while(getSn_CR(sn));   // wait to process the command...
   sock_is_sending |= (1<<sn);
 
   return len;
}
#else //for speed optimization, by lihan
int32_t send(uint8_t sn, uint8_t * buf, uint16_t len)
{
   uint8_t tmp=0;
   uint16_t freesize=0;
   
   // tx_bufferSize / 4 

   //if (len > 4096) len = 4096; // check size not to exceed MAX size.//
   //if (len > 8192) len = 8192; // check size not to exceed MAX siz
   //if (len > 16384) len = 16384; // check size not to exceed MAX size.
   //if (len > 32768) len = 32768; // check size not to exceed MAX size.
   #define __FREESIZE__(i)  1024 * i
   #define __FREESIZE__Value 8
   if (len > __FREESIZE__(__FREESIZE__Value)) len = __FREESIZE__(__FREESIZE__Value); // check size not to exceed MAX size.//tse

   while(1)
   {
      freesize = (uint16_t)getSn_TX_FSR(sn);
      if(len <= freesize) break;
   }
   wiz_send_data(sn, buf, len);
   setSn_CR(sn,Sn_CR_SEND);
 
   while(getSn_CR(sn));   // wait to process the command...
   sock_is_sending |= (1<<sn);
 
   return len;
}
#endif 
int32_t recv(uint8_t sn, uint8_t * buf, uint16_t len)
{
   uint8_t  tmp = 0;
   uint16_t recvsize = 0;
   /* 
    * The below codes can be omitted for optmization of speed
    */
   //CHECK_SOCKNUM();
   //CHECK_TCPMODE();
   //CHECK_SOCKDATA();
   /************/
 
   recvsize = getSn_RxMAX(sn); 
   if(recvsize < len) len = recvsize;
   while(1)
   {
      recvsize = (uint16_t)getSn_RX_RSR(sn);
      tmp = getSn_SR(sn);
      if (tmp != SOCK_ESTABLISHED && tmp != SOCK_CLOSE_WAIT)
      {
         if(tmp == SOCK_CLOSED) close(sn);
         return SOCKERR_SOCKSTATUS;
      }
      if(recvsize) break;
      if(sock_io_mode & (1<<sn)) return SOCK_BUSY;
   }
   if(recvsize < len) len = recvsize;
   wiz_recv_data(sn, buf, len); 
   setSn_CR(sn,Sn_CR_RECV); 
   while(getSn_CR(sn));  
   return len;
}


int32_t sendto_W5x00(uint8_t sn, uint8_t * buf, uint16_t len, uint8_t * addr, uint16_t port ){
   //static int32_t sendto_IO_6(uint8_t sn, uint8_t * buf, uint16_t len, uint8_t * addr, uint16_t port)
   printf("sendto_W5x00\r\n" ) ;
   return sendto_IO_6(sn,   buf,  len,   addr,  port,4);
}

int32_t sendto_W6x00(uint8_t sn, uint8_t * buf, uint16_t len, uint8_t * addr, uint16_t port, uint8_t addrlen ){
   printf("sendto_W6x00\r\n" ) ;
   //static int32_t sendto_IO_6(uint8_t sn, uint8_t * buf, uint16_t len, uint8_t * addr, uint16_t port)
   return sendto_IO_6( sn,  buf,  len,   addr,  port, addrlen);
}

static int32_t sendto_IO_6(uint8_t sn, uint8_t * buf, uint16_t len, uint8_t * addr, uint16_t port, uint8_t addrlen)
{
   uint8_t tmp = 0;
   uint8_t tcmd = Sn_CR_SEND;
   uint16_t freesize = 0;
   uint32_t taddr;
   /* 
    * The below codes can be omitted for optmization of speed
    */
   //CHECK_SOCKNUM();
   //CHECK_DGRAMMODE();
   /************/
   tmp = getSn_MR(sn);
   if(tmp != Sn_MR_MACRAW)
   {
       if (addrlen == 16)      // addrlen=16, Sn_MR_UDP6(1010), Sn_MR_UDPD(1110)), IPRAW6(1011)
      {
      #if (IPV6_EN) 
         if( tmp & 0x08)  
         {
            setSn_DIP6R(sn,addr);
            tcmd = Sn_CR_SEND6;
         }
         else
       #endif 
         return SOCKERR_SOCKMODE;
      } 
      else if(addrlen == 4)      // addrlen=4, Sn_MR_UDP4(0010), Sn_MR_UDPD(1110), IPRAW4(0011)
      {
         if(tmp == Sn_MR_UDP6 || tmp == Sn_MR_IPRAW6) return SOCKERR_SOCKMODE;
         setSn_DIPR(sn,addr);
         tcmd = Sn_CR_SEND;
      }
      else return SOCKERR_IPINVALID;
   }
   if((tmp & 0x03)==0x02)     // Sn_MR_UPD4(0010), Sn_MR_UDP6(1010), Sn_MR_UDPD(1110)
   {
      if(port){ setSn_DPORTR(sn, port);}
      else   return SOCKERR_PORTZERO;
   }
   #if !IPV6_EN
   CHECK_SOCKDATA();
   //M20140501 : For avoiding fatal error on memory align mismatched
   //if(*((uint32_t*)addr) == 0) return SOCKERR_IPINVALID;
   //{
      //uint32_t taddr;
      taddr = ((uint32_t)addr[0]) & 0x000000FF;
      taddr = (taddr << 8) + ((uint32_t)addr[1] & 0x000000FF);
      taddr = (taddr << 8) + ((uint32_t)addr[2] & 0x000000FF);
      taddr = (taddr << 8) + ((uint32_t)addr[3] & 0x000000FF);
   //}
   //
   //if(*((uint32_t*)addr) == 0) return SOCKERR_IPINVALID;
   if((taddr == 0) && ((getSn_MR(sn)&Sn_MR_MACRAW) != Sn_MR_MACRAW)) return SOCKERR_IPINVALID;
   if((port  == 0) && ((getSn_MR(sn)&Sn_MR_MACRAW) != Sn_MR_MACRAW)) return SOCKERR_PORTZERO;
   tmp = getSn_SR(sn);
   #endif 

   freesize = getSn_TxMAX(sn);
   if (len > freesize) len = freesize; // check size not to exceed MAX size.
  
   while(1)
   {
      freesize = getSn_TX_FSR(sn);
      if(getSn_SR(sn) == SOCK_CLOSED) return SOCKERR_SOCKCLOSED;
      if(len <= freesize) break;
      if( sock_io_mode & (1<<sn) ) return SOCK_BUSY;  
   }
   wiz_send_data(sn, buf, len);

   #if _WIZCHIP_ < 5500   //M20150401 : for WIZCHIP Errata #4, #5 (ARP errata)
      getSIPR((uint8_t*)&taddr);
      if(taddr == 0)
      {
         getSUBR((uint8_t*)&taddr);
         setSUBR((uint8_t*)"\x00\x00\x00\x00");
      }
      else taddr = 0;
   #endif
   #if IPV6_EN
   setSn_CR(sn,tcmd);
   #else
   setSn_CR(sn,Sn_CR_SEND);
   #endif 
   while(getSn_CR(sn));
  
   while(1)
   {
      tmp = getSn_IR(sn);
      if(tmp & Sn_IR_SENDOK)
      {
         setSn_IR(sn, Sn_IR_SENDOK);
         break;
      }  
      else if(tmp & Sn_IR_TIMEOUT)
      {
         setSn_IR(sn, Sn_IR_TIMEOUT);   
         return SOCKERR_TIMEOUT;
      }
   }  
   #if _WIZCHIP_ < 5500   //M20150401 : for WIZCHIP Errata #4, #5 (ARP errata)
      if(taddr) setSUBR((uint8_t*)&taddr);
   #endif
   return (int32_t)len;
}


int32_t recvfrom_W5x00(uint8_t sn, uint8_t * buf, uint16_t len, uint8_t * addr, uint16_t *port){
   //int32_t recvfrom_IO_6(uint8_t sn, uint8_t * buf, uint16_t len, uint8_t * addr, uint16_t *port)
   printf("recvfrom_W5x00\r\n" ) ;
   uint8_t *dummy ; 
   return recvfrom_IO_6(sn,   buf,  len,   addr,  port, dummy);
}

int32_t recvfrom_W6x00(uint8_t sn, uint8_t * buf, uint16_t len, uint8_t * addr, uint16_t *port, uint8_t *addrlen ){
   printf("recvfrom_W6x00\r\n" ) ;
   //int32_t recvfrom_IO_6(uint8_t sn, uint8_t * buf, uint16_t len, uint8_t * addr, uint16_t *port)
   return recvfrom_IO_6( sn,  buf,  len,   addr,  port, addrlen);
}
static int32_t recvfrom_IO_6(uint8_t sn, uint8_t * buf, uint16_t len, uint8_t * addr, uint16_t *port, uint8_t *addrlen)
{ 
   uint8_t  head[8]; // OR  head[2] by lihan, will be verify later
   uint16_t pack_len=0;
  
   /* 
    * The below codes can be omitted for optmization of speed
    */
   //CHECK_SOCKNUM();
   //CHECK_DGRAMMODE();
   //CHECK_SOCKDATA();
   /************/
  
   if(sock_remained_size[sn] == 0)
   {
      while(1)
      {
         pack_len = getSn_RX_RSR(sn);
         if(getSn_SR(sn) == SOCK_CLOSED) return SOCKERR_SOCKCLOSED;
         if(pack_len != 0)
         {
            sock_pack_info[sn] = PACK_NONE;
            break;
         } 
         if( sock_io_mode & (1<<sn) ) return SOCK_BUSY;
      };
      #if IPV6_EN
      /* First read 2 bytes of PACKET INFO in SOCKETn RX buffer*/
      wiz_recv_data(sn, head, 2);  
      setSn_CR(sn,Sn_CR_RECV);
      while(getSn_CR(sn));
      pack_len = head[0] & 0x07;
      pack_len = (pack_len << 8) + head[1];
    
      #endif 
      switch (getSn_MR(sn) & 0x0F)
      {
         case Sn_MR_UDP4 :
         case Sn_MR_UDP6:
         case Sn_MR_UDPD:
         case Sn_MR_IPRAW6:
         case Sn_MR_IPRAW4 : 
         #if IPV6_EN
            if(addr == 0) 
               return SOCKERR_ARG;
            
            sock_pack_info[sn] = head[0] & 0xF8;
           
            if(sock_pack_info[sn] & PACK_IPv6) {
               *addrlen = 16 ; 
              // inline_setAddrlen_W6x00 (16);
            }else{ 
               *addrlen = 4 ; 
              // inline_setAddrlen_W6x00 (4);
            }
            //wiz_recv_data(sn, addr, inline_CheckAddrlen_W6x00() );
            wiz_recv_data(sn, addr, *addrlen );
            setSn_CR(sn,Sn_CR_RECV);

            while(getSn_CR(sn));

         #else    
         #if _WIZCHIP_ == 5300
   		   if(mr1 & MR_FS)
   		   {
   		      addr[0] = head[1];
   		      addr[1] = head[0];
   		      addr[2] = head[3];
   		      addr[3] = head[2];
   		      *port = head[5];
   		      *port = (*port << 8) + head[4];
      			sock_remained_size[sn] = head[7];
      			sock_remained_size[sn] = (sock_remained_size[sn] << 8) + head[6];
   		   }
            else
            {
         #endif
            wiz_recv_data(sn, head, 8);
            setSn_CR(sn,Sn_CR_RECV);
            while(getSn_CR(sn)); 
            addr[0] = head[0];      
            addr[1] = head[1];
            addr[2] = head[2];
            addr[3] = head[3];
            *port = head[4];
            *port = (*port << 8) + head[5];
            sock_remained_size[sn] = head[6];
            sock_remained_size[sn] = (sock_remained_size[sn] << 8) + head[7];
         #if _WIZCHIP_ == 5300
            }
         #endif
            sock_pack_info[sn] = PACK_FIRST;
         #endif         
            break;
         case Sn_MR_MACRAW :
			pack_len-=2;
            if(pack_len > 1514) 
            {
               close(sn);
               return SOCKFATAL_PACKLEN;
            }
            break; 
         default:
            return SOCKERR_SOCKMODE;
            break;
      }
      #if IPV6_EN
      sock_remained_size[sn] = pack_len;
      sock_pack_info[sn] |= PACK_FIRST;
      if((getSn_MR(sn) & 0x03) == 0x02)  // Sn_MR_UDP4(0010), Sn_MR_UDP6(1010), Sn_MR_UDPD(1110)
      {
         /* Read port number of PACKET INFO in SOCKETn RX buffer */
         if(port==0) return SOCKERR_ARG;
         wiz_recv_data(sn, head, 2);
         *port = ( ((((uint16_t)head[0])) << 8) + head[1] );
         setSn_CR(sn,Sn_CR_RECV);
         while(getSn_CR(sn));   
      }
      #endif 
   }   
   
   if   (len < sock_remained_size[sn]) pack_len = len;
   else pack_len = sock_remained_size[sn];    
   wiz_recv_data(sn, buf, pack_len);
   setSn_CR(sn,Sn_CR_RECV);  
   /* wait to process the command... */
   while(getSn_CR(sn)) ;
 
   sock_remained_size[sn] -= pack_len; 
   if(sock_remained_size[sn] != 0) sock_pack_info[sn] |= PACK_REMAINED; 
   else sock_pack_info[sn] |= PACK_COMPLETED; 
 
   return pack_len;
}

int8_t ctlsocket(uint8_t sn, ctlsock_type cstype, void* arg)
{
   uint8_t tmp = 0;
   CHECK_SOCKNUM();
   tmp = *((uint8_t*)arg); 
   switch(cstype)
   {
      case CS_SET_IOMODE:
         if(tmp == SOCK_IO_NONBLOCK)  sock_io_mode |= (1<<sn);
         else if(tmp == SOCK_IO_BLOCK) sock_io_mode &= ~(1<<sn);
         else return SOCKERR_ARG;
         break;
      case CS_GET_IOMODE: 
         *((uint8_t*)arg) = (uint8_t)((sock_io_mode >> sn) & 0x0001);
         break;
      case CS_GET_MAXTXBUF:
         *((uint16_t*)arg) = getSn_TxMAX(sn);
         break;
      case CS_GET_MAXRXBUF:  
         *((uint16_t*)arg) = getSn_RxMAX(sn);
         break;
      case CS_CLR_INTERRUPT:
         if( tmp > SIK_ALL) return SOCKERR_ARG;
         setSn_IR(sn,tmp);
         break;
      case CS_GET_INTERRUPT:
         *((uint8_t*)arg) = getSn_IR(sn);
         break;
#if _WIZCHIP_ != 5100
      case CS_SET_INTMASK:
         if( tmp > SIK_ALL) return SOCKERR_ARG;
         setSn_IMR(sn,tmp);
         break;
      case CS_GET_INTMASK:
         *((uint8_t*)arg) = getSn_IMR(sn);
         break;
#endif
#if IPV6_EN
      case CS_SET_PREFER:
    	  if((tmp & 0x03) == 0x01) return SOCKERR_ARG;
    	  setSn_PSR(sn, tmp);
    	  break;
      case CS_GET_PREFER:
    	  *(uint8_t*) arg = getSn_PSR(sn);
    	  break;
#endif 
      default:
         return SOCKERR_ARG;
   }
   return SOCK_OK;
}

int8_t setsockopt(uint8_t sn, sockopt_type sotype, void* arg)
{
   CHECK_SOCKNUM();
   switch(sotype)
   {
      case SO_TTL:
         setSn_TTL(sn,*(uint8_t*)arg);
         break;
      case SO_TOS:
         setSn_TOS(sn,*(uint8_t*)arg);
         break;
      case SO_MSS:
         setSn_MSSR(sn,*(uint16_t*)arg);
         break;
      case SO_DESTIP:
      #if IPV6_EN
         if(((wiz_IPAddress*)arg)->len == 16) 
            setSn_DIP6R(sn, ((wiz_IPAddress*)arg)->ip);
         else 
      #endif 
         setSn_DIPR(sn, (uint8_t*)arg);
         break;
      case SO_DESTPORT:
         setSn_DPORTR(sn, *(uint16_t*)arg);
         break;
#if _WIZCHIP_ != 5100 
      case SO_KEEPALIVESEND:
         CHECK_TCPMODE();   
#if _WIZCHIP_ > 5200
         if(getSn_KPALVTR(sn) != 0) return SOCKERR_SOCKOPT;
#endif 
         setSn_CR(sn,Sn_CR_SEND_KEEP);
         while(getSn_CR(sn) != 0)
         {     
            if (getSn_IR(sn) & Sn_IR_TIMEOUT)
            {
               setSn_IR(sn, Sn_IR_TIMEOUT);
               return SOCKERR_TIMEOUT;
            }
         }
         break;
#if !( (_WIZCHIP_ == 5100) || (_WIZCHIP_ == 5200) )
      case SO_KEEPALIVEAUTO:
         CHECK_TCPMODE();
         setSn_KPALVTR(sn,*(uint8_t*)arg);
         break;   
#endif 
#endif 
      default:
         return SOCKERR_ARG;
   } 
   return SOCK_OK;
}

int8_t getsockopt(uint8_t sn, sockopt_type sotype, void* arg)
{
   CHECK_SOCKNUM();
   switch(sotype)
   {
      case SO_FLAG:
      #if IPV6_EN 
         *(uint8_t*)arg = (getSn_MR(sn) & 0xF0) | (getSn_MR2(sn)) | ((uint8_t)(((sock_io_mode >> sn) & 0x0001) << 3));
      #endif
         *(uint8_t*)arg = getSn_MR(sn) & 0xF0;
         break;
      case SO_TTL:
         *(uint8_t*) arg = getSn_TTL(sn);
         break;
      case SO_TOS:
         *(uint8_t*) arg = getSn_TOS(sn);
         break;
      case SO_MSS: 
         *(uint16_t*) arg = getSn_MSSR(sn);
         break;
      case SO_DESTIP:
      #if IPV6_EN
         CHECK_TCPMODE();
         if(getSn_ESR(sn) & TCPSOCK_MODE) //IPv6 ?
         {
            getSn_DIP6R(sn, ((wiz_IPAddress*)arg)->ip);
            ((wiz_IPAddress*)arg)->len = 16;
         } 
         else
         {
            getSn_DIPR(sn, ((wiz_IPAddress*)arg)->ip);
            ((wiz_IPAddress*)arg)->len = 4;
         } 
         break;
      #else 
         getSn_DIPR(sn, (uint8_t*)arg);
      #endif 
      case SO_DESTPORT:  
         *(uint16_t*) arg = getSn_DPORTR(sn);
         break; 
#if  _WIZCHIP_ > 5200  
      case SO_KEEPALIVEAUTO:
         CHECK_TCPMODE();
         *(uint16_t*) arg = getSn_KPALVTR(sn);
         break;
#endif 
      case SO_SENDBUF:
         *(uint16_t*) arg = getSn_TX_FSR(sn);
         break;
      case SO_RECVBUF:
         *(uint16_t*) arg = getSn_RX_RSR(sn);
         break;
      case SO_STATUS:
         *(uint8_t*) arg = getSn_SR(sn);
         break;
#if IPV6_EN
      case SO_EXTSTATUS:
         CHECK_TCPMODE();
         *(uint8_t*) arg = getSn_ESR(sn) & 0x07;
         break;
      case SO_REMAINSIZE:
         if(getSn_MR(sn)==SOCK_CLOSED) return SOCKERR_SOCKSTATUS;
         if(getSn_MR(sn) & 0x01)   *(uint16_t*)arg = getSn_RX_RSR(sn);
         else                      *(uint16_t*)arg = sock_remained_size[sn];
         break;
      case SO_PACKINFO:
         if(getSn_MR(sn)==SOCK_CLOSED) return SOCKERR_SOCKSTATUS;
         if(getSn_MR(sn) & 0x01)       return SOCKERR_SOCKMODE;
         else *(uint8_t*)arg = sock_pack_info[sn];
         break;
      case SO_MODE:
         *(uint8_t*) arg = 0x0F & getSn_MR(sn);
         break;
#else 
      case SO_REMAINSIZE:
               if(getSn_MR(sn) & Sn_MR_TCP)
                  *(uint16_t*)arg = getSn_RX_RSR(sn);
               else
                  *(uint16_t*)arg = sock_remained_size[sn];
               break;
      case SO_PACKINFO  :
               //CHECK_SOCKMODE(Sn_MR_TCP);
      #if _WIZCHIP_ != 5300
               if((getSn_MR(sn) == Sn_MR_TCP))
                  return SOCKERR_SOCKMODE;
      #endif
               *(uint8_t*)arg = sock_pack_info[sn];
               break;

#endif 
      default:
         return SOCKERR_SOCKOPT;
   }
   return SOCK_OK;
}

#if IPV6_EN 
int16_t peeksockmsg(uint8_t sn, uint8_t* submsg, uint16_t subsize)
{
   uint32_t rx_ptr = 0;
   uint16_t i = 0, sub_idx = 0;

   if( (getSn_RX_RSR(sn) > 0) && (subsize > 0) )
   {
       rx_ptr = ((uint32_t)getSn_RX_RD(sn) << 8)  + WIZCHIP_RXBUF_BLOCK(sn);
       sub_idx = 0;
       for(i = 0; i < getSn_RX_RSR(sn) ; i++)
       {
          if(WIZCHIP_READ(rx_ptr) == submsg[sub_idx])
          {
              sub_idx++;
              if(sub_idx == subsize) return (i + 1 - sub_idx);
          }
          else sub_idx = 0;
          rx_ptr = WIZCHIP_OFFSET_INC(rx_ptr,1);
       }
   }
   return -1;
}


#endif 

